// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "ppl/cv/arm/crop.h"

#include <tuple>
#include <sstream>

#include "opencv2/core.hpp"
#include "gtest/gtest.h"

#include "utility/infrastructure.hpp"

#define INT_PARAM_TO_FLOAT(x) ((x) / 10.0f)

using Parameters = std::tuple<int, int, int, cv::Size>;
inline std::string convertToStringCrop(const Parameters& parameters)
{
    std::ostringstream formatted;

    int left = std::get<0>(parameters);
    formatted << "Left" << left << "_";

    int top = std::get<1>(parameters);
    formatted << "Top" << top << "_";

    float int_scale = std::get<2>(parameters);
    formatted << "IntScale" << int_scale << "_";

    cv::Size size = std::get<3>(parameters);
    formatted << size.width << "x";
    formatted << size.height;

    return formatted.str();
}

template <typename T, int channels>
class PplCvArmCropTest : public ::testing::TestWithParam<Parameters> {
public:
    PplCvArmCropTest()
    {
        const Parameters& parameters = GetParam();
        left = std::get<0>(parameters);
        top = std::get<1>(parameters);
        scale = INT_PARAM_TO_FLOAT(std::get<2>(parameters));
        size = std::get<3>(parameters);
    }

    ~PplCvArmCropTest() {}

    bool apply();

private:
    int left;
    int top;
    float scale;
    cv::Size size;
};

template <typename T, int channels>
bool PplCvArmCropTest<T, channels>::apply()
{
    int src_height = size.height * 2;
    int src_width = size.width * 2;
    cv::Mat src = createSourceImage(src_height, src_width, CV_MAKETYPE(cv::DataType<T>::depth, channels));
    cv::Mat dst(size.height, size.width, CV_MAKETYPE(cv::DataType<T>::depth, channels));
    cv::Mat cv_dst(size.height, size.width, CV_MAKETYPE(cv::DataType<T>::depth, channels));

    cv::Rect roi(left, top, size.width, size.height);
    cv::Mat croppedImage = src(roi);
    croppedImage.copyTo(cv_dst);
    cv_dst = cv_dst * scale;

    ppl::cv::arm::Crop<T, channels>(src.rows,
                                    src.cols,
                                    src.step / sizeof(T),
                                    (T*)src.data,
                                    dst.rows,
                                    dst.cols,
                                    dst.step / sizeof(T),
                                    (T*)dst.data,
                                    left,
                                    top,
                                    scale);

    float epsilon;
    if (sizeof(T) == 1) {
        epsilon = EPSILON_1F;
    } else {
        epsilon = EPSILON_E6;
    }
    bool identity = checkMatricesIdentity<T>(cv_dst, dst, epsilon);

    return identity;
}

#define UNITTEST(T, channels)                                                                                      \
    using PplCvArmCropTest_##T##_##channels = PplCvArmCropTest<T, channels>;                                       \
    TEST_P(PplCvArmCropTest_##T##_##channels, Standard)                                                            \
    {                                                                                                              \
        bool identity = this->apply();                                                                             \
        EXPECT_TRUE(identity);                                                                                     \
    }                                                                                                              \
                                                                                                                   \
    INSTANTIATE_TEST_CASE_P(IsEqual,                                                                               \
                            PplCvArmCropTest_##T##_##channels,                                                     \
                            ::testing::Combine(::testing::Values(0, 11, 187),                                      \
                                               ::testing::Values(0, 11, 187),                                      \
                                               ::testing::Values(3, 10, 15),                                       \
                                               ::testing::Values(cv::Size{321, 240},                               \
                                                                 cv::Size{642, 480},                               \
                                                                 cv::Size{1283, 720},                              \
                                                                 cv::Size{1934, 1080},                             \
                                                                 cv::Size{320, 240},                               \
                                                                 cv::Size{640, 480},                               \
                                                                 cv::Size{1280, 720},                              \
                                                                 cv::Size{1920, 1080})),                           \
                            [](const testing::TestParamInfo<PplCvArmCropTest_##T##_##channels::ParamType>& info) { \
                                return convertToStringCrop(info.param);                                            \
                            });

UNITTEST(uint8_t, 1)
UNITTEST(uint8_t, 3)
UNITTEST(uint8_t, 4)
UNITTEST(float, 1)
UNITTEST(float, 3)
UNITTEST(float, 4)